_ = require 'lodash'
chance = new require('chance')()
crypto = require('crypto')

PayService = require './PayService'
log = require '../log'
config = require '../config'
util = require '../util'

# APP支付的的微信下单
exports.gPreparePaymentByApp = (ctx, amount, payFor, business, businessId, clientIP)->
    payment = yield from PayService.gCreatePayment(ctx, PayService.Providers.WeixinPay, amount, business, businessId)
    prePay = yield from gPreparePayment ctx, payment._id, amount, payFor, 'APP', clientIP
    return {payment, prePay}

# 扫码支付的的微信下单
exports.gPreparePaymentByQrCode = (ctx, amount, payFor, business, businessId, clientIP)->
    payment = yield from PayService.gCreatePayment(ctx, PayService.Providers.WeixinPay, amount, business, businessId)
    prePay = yield from gPreparePayment ctx, payment._id, amount, payFor, 'NATIVE', clientIP
    return {payment, prePay}

# 处理微信支付通知
exports.gWeixinCallback = (ctx, data)->
    data = data.xml

    # TODO 验证签名

    paymentId = data.out_trade_no[0]
    responseCode = data.result_code[0]
    providerFlowNo = data.transaction_id[0]
    buyer = data.openid[0]
    amount = parseInt(data.total_fee[0], 10)
    returnCode = data.return_code[0]

    state = if returnCode == 'SUCCESS' and responseCode == 'SUCCESS'
        PayService.states.Success
    else if responseCode == 'FAIL'
        PayService.states.Fail
    else null

    if state
        yield from PayService.gUpdatePaymentState(ctx, paymentId, state, providerFlowNo, amount, buyer)
    else
        log.pay.info 'Not process callback state, Weixin', {returnCode, responseCode, paymentId, providerFlowNo}

# 统一下单接口
gPreparePayment = (ctx, paymentId, amount, payFor, tradeType, clientIP)->
    clientIP = '127.0.0.1' unless /^\d+\.\d+\.\d+\.\d+$/.test clientIP.length
    req = buildPreparePayRequestObject(paymentId, payFor, amount, tradeType, clientIP)
    # log.pay.debug 'got req', req

    xml = util.objectToXML(req)
    # log.pay.debug 'got xml', xml.toString()

    r = yield from gRequestUnifiedOrder(xml.toString())
    if r?.statusCode != 200
        log.pay.error 'Weixin PrePay, Bad Code:', {status: r?.statusCode, message: r?.statusMessage}
        throw new errors.SystemError 'ThirdPayOrderFail', '请求支付失败'

    log.pay.info 'Weixin Prepay response', {res: r.body, paymentId}

    res = yield util.pParseXML r.body
    # TODO 验证签名
    returnCode = res?.xml?.return_code?[0]
    returnMessage = res?.xml?.return_msg?[0]
    resultCode = res?.xml?.result_code?[0]
    if returnCode == 'SUCCESS' and resultCode == 'SUCCESS'
        prepayId = res?.xml?.prepay_id[0]
        codeUrl = res?.xml?.code_url[0]
        return {prepayId, codeUrl}
    else
        log.pay.error 'Weixin Prepay, Bad Result:', {returnCode, returnMessage, resultCode, paymentId}
        throw new errors.SystemError 'ThirdPayOrderFail', '请求支付失败'

buildPreparePayRequestObject = (paymentId, payFor, amount, tradeType, clientIP)->
    req = {}
    #  公众账号ID
    req.appid = config.pay.weixin.APP_ID
    # 商户号
    req.mch_id = config.pay.weixin.mch_id
    # 设备号（可选）注意：PC网页或公众号内支付请传"WEB"
    req.device_info = 'WEB'
    # 随机字符串。不长于32位
    req.nonce_str = '' + chance.natural()
    # 商品描述，32个字符
    req.body = payFor
    # 商品详情，可选，String(8192)
    # req.detail = 'Test 00'
    # 附加数据 String(127)
    req.attach = 'no'
    # 商户订单号 String(32)
    req.out_trade_no = paymentId
    # 总金额（分）
    req.total_fee = '' + amount
    # 终端IP
    req.spbill_create_ip = clientIP
    # 通知地址
    req.notify_url = config.pay.weixin.ourCallbackUrl
    # 交易类型
    req.trade_type = tradeType

    # log.pay.debug 'req: ', req

    # 签名
    req.sign = signObject(req)
    req

signObject = (req) ->
    pairs = _.toPairs(req)
    pairs = _.sortBy pairs, (p) -> p[0]

    stringPairs = for pair in pairs
        pair[0] + '=' + pair[1]
    reqString = stringPairs.join('&') + '&key=' + config.pay.weixin.APISecret
    # console.log 'reqString: ' + reqString

    signature = crypto.createHash('md5').update(new Buffer(reqString, "UTF-8")).digest('hex').toUpperCase()
    # console.log 'sign: ' + sign
    signature

gRequestUnifiedOrder = (body)->
    try
        yield promiseRequest({
            url: 'https://api.mch.weixin.qq.com/pay/unifiedorder', method: 'POST', body: body
        })
    catch e
        log.pay.error e, "Request Weixin unified order"
        return null